<template><div><h1 id="key详解" tabindex="-1"><a class="header-anchor" href="#key详解" aria-hidden="true">#</a> <code v-pre>Key</code>详解</h1>
<p>我们平时一定接触过很多的 <code v-pre>Widget</code>，比如 <code v-pre>Container</code>、<code v-pre>Row</code>、<code v-pre>Column</code> 等，它们在我们绘制界面的过程 中发挥着重要的作用。但是不知道你有没有注意到，在几乎每个 <code v-pre>Widget</code> 的构造函数中，都有一个共同 的参数，它们通常在参数列表的第一个，那就是 <code v-pre>Key</code>。</p>
<p>在<code v-pre>Flutter</code>中，<code v-pre>Key</code>是不能重复使用的，所以<code v-pre>Key</code>一般用来做唯一标识。组件在更新的时候，其状态的保 存主要是通过判断组件的类型或者<code v-pre>key</code>值是否一致。因此，当各组件的类型不同的时候，类型已经足够 用来区分不同的组件了，此时我们可以不必使用<code v-pre>key</code>。但是如果同时存在多个同一类型的控件的时候， 此时类型已经无法作为区分的条件了，我们就需要使用到<code v-pre>key</code>。</p>
<h2 id="_1-没有-key-会发生什么奇怪现象" tabindex="-1"><a class="header-anchor" href="#_1-没有-key-会发生什么奇怪现象" aria-hidden="true">#</a> 1. 没有 Key 会发生什么奇怪现象</h2>
<p>如下面例： 定义了一个<code v-pre>StatefulWidget</code>的<code v-pre>Box</code>，点击<code v-pre>Box</code>的时候可以改变<code v-pre>Box</code>里面的数字，当我们重新对<code v-pre>Box</code>排序的时候<code v-pre>Flutter</code>就无法识别到<code v-pre>Box</code>的变化了， 这是什么原因呢？</p>
<p><img src="@source/blog/Flutter/assets/image-20230514194031122.png" alt="image-20230514194031122"></p>
<div class="language-dart line-numbers-mode" data-ext="dart"><pre v-pre class="language-dart"><code><span class="token keyword">import</span> <span class="token string-literal"><span class="token string">'package:flutter/material.dart'</span></span><span class="token punctuation">;</span>

<span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token function">runApp</span><span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token class-name">MyApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">MyApp</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token class-name">MyApp</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token class-name">Key</span><span class="token operator">?</span> key<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// This widget is the root of your application.</span>
  <span class="token metadata function">@override</span>
  <span class="token class-name">Widget</span> <span class="token function">build</span><span class="token punctuation">(</span><span class="token class-name">BuildContext</span> context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token class-name">MaterialApp</span><span class="token punctuation">(</span>
      title<span class="token punctuation">:</span> <span class="token string-literal"><span class="token string">'Flutter Demo'</span></span><span class="token punctuation">,</span>
      theme<span class="token punctuation">:</span> <span class="token class-name">ThemeData</span><span class="token punctuation">(</span>
        primarySwatch<span class="token punctuation">:</span> <span class="token class-name">Colors</span><span class="token punctuation">.</span>blue<span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
      home<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token class-name">MyHomePage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">MyHomePage</span> <span class="token keyword">extends</span> <span class="token class-name">StatefulWidget</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token class-name">MyHomePage</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token keyword">super</span><span class="token punctuation">.</span>key<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token metadata function">@override</span>
  <span class="token class-name">State</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">MyHomePage</span><span class="token punctuation">></span></span> <span class="token function">createState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">_MyHomePageState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> _MyHomePageState <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">MyHomePage</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span>
  <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Widget</span><span class="token punctuation">></span></span> list <span class="token operator">=</span> <span class="token punctuation">[</span>
    <span class="token class-name">Box</span><span class="token punctuation">(</span>
      color<span class="token punctuation">:</span> <span class="token class-name">Colors</span><span class="token punctuation">.</span>blue<span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token class-name">Box</span><span class="token punctuation">(</span>
      color<span class="token punctuation">:</span> <span class="token class-name">Colors</span><span class="token punctuation">.</span>red<span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token class-name">Box</span><span class="token punctuation">(</span>
      color<span class="token punctuation">:</span> <span class="token class-name">Colors</span><span class="token punctuation">.</span>orange<span class="token punctuation">,</span>
    <span class="token punctuation">)</span>
  <span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token metadata function">@override</span>
  <span class="token class-name">Widget</span> <span class="token function">build</span><span class="token punctuation">(</span><span class="token class-name">BuildContext</span> context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token class-name">Scaffold</span><span class="token punctuation">(</span>
      floatingActionButton<span class="token punctuation">:</span> <span class="token class-name">FloatingActionButton</span><span class="token punctuation">(</span>
        onPressed<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token function">setState</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            list<span class="token punctuation">.</span><span class="token function">shuffle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//打乱list的顺序</span>
          <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        child<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token class-name">Icon</span><span class="token punctuation">(</span><span class="token class-name">Icons</span><span class="token punctuation">.</span>refresh<span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
      appBar<span class="token punctuation">:</span> <span class="token class-name">AppBar</span><span class="token punctuation">(</span>
        title<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token class-name">Text</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">'Title'</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
      body<span class="token punctuation">:</span> <span class="token class-name">Center</span><span class="token punctuation">(</span>
        child<span class="token punctuation">:</span> <span class="token class-name">Column</span><span class="token punctuation">(</span>
          mainAxisAlignment<span class="token punctuation">:</span> <span class="token class-name">MainAxisAlignment</span><span class="token punctuation">.</span>center<span class="token punctuation">,</span>
          children<span class="token punctuation">:</span> list<span class="token punctuation">,</span>
        <span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Box</span> <span class="token keyword">extends</span> <span class="token class-name">StatefulWidget</span> <span class="token punctuation">{</span>
  <span class="token class-name">Color</span> color<span class="token punctuation">;</span>
  <span class="token class-name">Box</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token keyword">super</span><span class="token punctuation">.</span>key<span class="token punctuation">,</span> required <span class="token keyword">this</span><span class="token punctuation">.</span>color<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token metadata function">@override</span>
  <span class="token class-name">State</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Box</span><span class="token punctuation">></span></span> <span class="token function">createState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">_BoxState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> _BoxState <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Box</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span>
  int _count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  <span class="token metadata function">@override</span>
  <span class="token class-name">Widget</span> <span class="token function">build</span><span class="token punctuation">(</span><span class="token class-name">BuildContext</span> context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token class-name">SizedBox</span><span class="token punctuation">(</span>
      height<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">,</span>
      width<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">,</span>
      child<span class="token punctuation">:</span> <span class="token class-name">ElevatedButton</span><span class="token punctuation">(</span>
        style<span class="token punctuation">:</span> <span class="token class-name">ButtonStyle</span><span class="token punctuation">(</span>
            backgroundColor<span class="token punctuation">:</span> <span class="token class-name">MaterialStateProperty</span><span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span>widget<span class="token punctuation">.</span>color<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        onPressed<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token function">setState</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            _count<span class="token operator">++</span><span class="token punctuation">;</span>
          <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        child<span class="token punctuation">:</span> <span class="token class-name">Center</span><span class="token punctuation">(</span>
          child<span class="token punctuation">:</span> <span class="token class-name">Text</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"</span><span class="token interpolation"><span class="token punctuation">$</span><span class="token expression">_count</span></span><span class="token string">"</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>运行后我们发现改变<code v-pre>list Widget</code>顺序后，<code v-pre>Widget</code>颜色会变化，但是每个<code v-pre>Widget</code>里面的文本内容并没有变化，为什么会这样呢？当我们<code v-pre>List</code>重新排序后<code v-pre>Flutter</code>检测到了<code v-pre>Widget</code>的顺序变化，所以重新绘制<code v-pre>List Widget</code>，但是<code v-pre>Flutter </code>发现<code v-pre>List Widget</code>里面的元素没有变化，所以就没有改变<code v-pre>Widget</code>里面的内容。</p>
<p>把<code v-pre>List </code>里面的<code v-pre>Box</code>的颜色改成一样，这个时候您重新对list进行排序，就很容易理解了。重新排序后虽然 执行了<code v-pre>setState</code>，但是代码和以前是一样的，所以<code v-pre>Flutter</code>不会重构<code v-pre>List Widget</code>里面的内容, 也就是 <code v-pre>Flutter</code>没法通过<code v-pre>Box</code>里面传入的参数来识别<code v-pre>Box</code>是否改变。如果要让<code v-pre>FLutter</code>能识别到<code v-pre>List Widget</code>子元素 的改变，就需要给每个<code v-pre>Box</code>指定一个<code v-pre>key</code>。</p>
<div class="language-dart line-numbers-mode" data-ext="dart"><pre v-pre class="language-dart"><code>list <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token class-name">Box</span><span class="token punctuation">(</span>
    key<span class="token punctuation">:</span> <span class="token class-name">ValueKey</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"1"</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    color<span class="token punctuation">:</span> <span class="token class-name">Colors</span><span class="token punctuation">.</span>yellow
  <span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token class-name">Box</span><span class="token punctuation">(</span>
    key<span class="token punctuation">:</span> <span class="token class-name">UniqueKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    color<span class="token punctuation">:</span> <span class="token class-name">Colors</span><span class="token punctuation">.</span>green<span class="token punctuation">,</span>
  <span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token class-name">Box</span><span class="token punctuation">(</span>
    key<span class="token punctuation">:</span> <span class="token class-name">ObjectKey</span><span class="token punctuation">(</span><span class="token class-name">Box</span><span class="token punctuation">(</span>color<span class="token punctuation">:</span> <span class="token class-name">Colors</span><span class="token punctuation">.</span>pink<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    color<span class="token punctuation">:</span> <span class="token class-name">Colors</span><span class="token punctuation">.</span>pink<span class="token punctuation">,</span>
  <span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="_2-localkey、globalkey" tabindex="-1"><a class="header-anchor" href="#_2-localkey、globalkey" aria-hidden="true">#</a> 2. <code v-pre>LocalKey</code>、<code v-pre>GlobalKey</code></h2>
<p>在<code v-pre>Flutter</code>中，<strong><code v-pre>Key</code>是不能重复使用的</strong>，所以<code v-pre>Key</code>一般用来做唯一标识。组件在更新的时候，其状态的保 存主要是通过判断<strong>组件的类型或者<code v-pre>key</code>值</strong>是否一致。因此，当各组件的类型不同的时候，类型已经足够 用来区分不同的组件了，此时我们可以不必使用<code v-pre>key</code>。但是如果同时存在多个同一类型的控件的时候， 此时类型已经无法作为区分的条件了，我们就需要使用到<code v-pre>key</code>。</p>
<p><strong>Flutter key子类包含 <code v-pre>LocalKey</code> 和 <code v-pre>GlobalKey</code> 。</strong></p>
<ul>
<li>局部键（<code v-pre>LocalKey</code>）：<code v-pre>ValueKey</code>、<code v-pre>ObjectKey</code>、<code v-pre>UniqueKey </code></li>
<li>全局键（<code v-pre>GlobalKey</code>）： <code v-pre>GlobalKey</code>、<code v-pre>GlobalObjectKey</code></li>
</ul>
<p><img src="@source/blog/Flutter/assets/image-20230514194748477.png" alt="image-20230514194748477"></p>
<p><code v-pre>ValueKey </code>（值<code v-pre>key</code>）把一个值作为<code v-pre>key</code> ，<code v-pre>UniqueKey</code>（唯一<code v-pre>key</code>）程序生成唯一的<code v-pre>Key</code>，当我们不知道 如何指定<code v-pre>ValueKey</code>的时候就可以使用<code v-pre>UniqueKey</code>，<code v-pre>ObjectKey</code>（对象<code v-pre>key</code>）把一个对象实例作为<code v-pre>key</code>。</p>
<p><code v-pre>ValueKey </code>（值<code v-pre>key</code>）把一个值作为<code v-pre>key</code> ，<code v-pre>UniqueKey</code>（唯一<code v-pre>key</code>）程序生成唯一的<code v-pre>Key</code>，当我们不知道 如何指定<code v-pre>ValueKey</code>的时候就可以使用<code v-pre>UniqueKey</code>，<code v-pre>ObjectKey</code>（对象<code v-pre>key</code>）把一个对象实例作为<code v-pre>key</code>。</p>
<h2 id="_3-globalkey-获取子组件" tabindex="-1"><a class="header-anchor" href="#_3-globalkey-获取子组件" aria-hidden="true">#</a> 3. <code v-pre>GlobalKey</code> 获取子组件</h2>
<ol>
<li><code v-pre>globalKey.currentState</code> 可以获取子组件的状态，执行子组件的方法，</li>
<li><code v-pre>globalKey.currentWidget</code>可以获 取子组件的属性，</li>
<li><code v-pre>globalKey.currentContext!.findRenderObject()</code>可以获取渲染的属性。</li>
</ol>
<h2 id="_4-widget-tree、element-tree-和-renderobject-tree" tabindex="-1"><a class="header-anchor" href="#_4-widget-tree、element-tree-和-renderobject-tree" aria-hidden="true">#</a> 4. <code v-pre>Widget Tree</code>、<code v-pre>Element Tree </code>和 <code v-pre>RenderObject Tree</code></h2>
<p><code v-pre>Flutter</code>应用是由是<code v-pre>Widget Tree</code>、<code v-pre>Element Tree</code> 和 <code v-pre>RenderObject Tree</code>组成 <code v-pre>Widget</code>可以理解成一个类，<code v-pre>Element</code>可以理解成<code v-pre>Widget</code>的实例，<code v-pre>Widget</code>与<code v-pre>Element</code>的关系可以是一对 多，一份配置可以创造多个<code v-pre>Element</code>实例</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code v-pre>Widget</code></td>
<td><code v-pre>Widget</code>就是一个类， 是<code v-pre>Element</code>的配置信息。与<code v-pre>Element</code>的关系可以是一对 多，一份配置可以创造多个<code v-pre>Element</code>实例</td>
</tr>
<tr>
<td><code v-pre>Element</code></td>
<td><code v-pre>Widget </code>的实例化，内部持有<code v-pre>Widget</code>和<code v-pre>RenderObject</code>。</td>
</tr>
<tr>
<td><code v-pre>RenderObject</code></td>
<td>负责渲染绘制</td>
</tr>
</tbody>
</table>
<p>默认情况下面，当<code v-pre>Flutter</code>同一个 <code v-pre>Widget</code>的大小，顺序变化的时候，<code v-pre>FLutter</code>不会改变<code v-pre>Widget</code>的<code v-pre>state</code>。</p>
</div></template>


